/**
* TUTORIAL OPENCV
* EXEMPLO 1: Manipulando imagens
*
* @author   MALF - malf@tecgraf.puc-rio.br
* @date     29/06/07
*/
#include "cvconfig.h"
#include <opencv/cv.h>
#include <opencv/cvaux.h>
#include <opencv/highgui.h>
#include <stdio.h>
#include <iostream>
#include <math.h>
#include "MySIFT/SIFT.h"

using namespace cv;
using namespace std;

Vector<IplImage *> images;

my::SIFT *sift;

void testeCarregaImagem()
{
    /* Carregar imagem de entrada */
    IplImage *img = cvLoadImage("tecgraf.jpg");
    /* Criar uma janela para exibir a imagem */
    cvNamedWindow("img_wnd", 0);
    /* Exibindo a imagem na janela criada */
    cvShowImage("img_wnd", img);
    /* Redimensionar a janela */
    cvResizeWindow("img_wnd", 320, 240);
    /* Mover a janela para o canto superior esquedo coordenada(0,0) da tela */
    cvMoveWindow("img_wnd", 0, 0);
    /* Manter o programa ativo enquanto nenhuma tecla for pressionada */
    int keyCode = cvWaitKey();
    /* Desalocar imagem */
    cvReleaseImage(&img);
    /* Desalocar janela*/
    cvDestroyWindow("img_wnd");
}

void cvShowManyImages(char* title, int nArgs, ...) {

    // img - Used for getting the arguments
    IplImage *img;

    // DispImage - the image in which input images are to be copied
    IplImage *DispImage;

    int size;
    int i;
    int m, n;
    int x, y;

    // w - Maximum number of images in a row
    // h - Maximum number of images in a column
    int w, h;

    // scale - How much we have to resize the image
    float scale;
    int max;

    // If the number of arguments is lesser than 0 or greater than 12
    // return without displaying
    if(nArgs <= 0) {
        printf("Number of arguments too small....\n");
        return;
    }
    else if(nArgs > 12) {
        printf("Number of arguments too large....\n");
        return;
    }
    // Determine the size of the image,
    // and the number of rows/cols
    // from number of arguments
    else if (nArgs == 1) {
        w = h = 1;
        size = 300;
    }
    else if (nArgs == 2) {
        w = 2; h = 1;
        size = 300;
    }
    else if (nArgs == 3 || nArgs == 4) {
        w = 2; h = 2;
        size = 300;
    }
    else if (nArgs == 5 || nArgs == 6) {
        w = 3; h = 2;
        size = 200;
    }
    else if (nArgs == 7 || nArgs == 8) {
        w = 4; h = 2;
        size = 200;
    }
    else {
        w = 4; h = 3;
        size = 150;
    }

    // Create a new 3 channel image
    DispImage = cvCreateImage( cvSize(100 + size*w, 60 + size*h), 8, 3 );

    // Used to get the arguments passed
    va_list args;
    va_start(args, nArgs);

    // Loop for nArgs number of arguments
    for (i = 0, m = 20, n = 20; i < nArgs; i++, m += (20 + size)) {

        // Get the Pointer to the IplImage
        img = va_arg(args, IplImage*);

        // Check whether it is NULL or not
        // If it is NULL, release the image, and return
        if(img == 0) {
            printf("Invalid arguments");
            cvReleaseImage(&DispImage);
            return;
        }

        // Find the width and height of the image
        x = img->width;
        y = img->height;

        // Find whether height or width is greater in order to resize the image
        max = (x > y)? x: y;

        // Find the scaling factor to resize the image
        scale = (float) ( (float) max / size );

        // Used to Align the images
        if( i % w == 0 && m!= 20) {
            m = 20;
            n+= 20 + size;
        }

        // Set the image ROI to display the current image
        cvSetImageROI(DispImage, cvRect(m, n, (int)( x/scale ), (int)( y/scale )));

        // Resize the input image and copy the it to the Single Big Image
        cvResize(img, DispImage);

        // Reset the ROI in order to display the next image
        cvResetImageROI(DispImage);
    }

    // Create a new window, and show the Single Big Image
    cvNamedWindow( title, 1 );
    cvShowImage( title, DispImage);

    cvWaitKey();
    cvDestroyWindow(title);

    // End the number of arguments
    va_end(args);

    // Release the Image Memory
    cvReleaseImage(&DispImage);
}


void exibirJanelaComImagem(IplImage *img)
{
    cvNamedWindow("img_wnd", 0);
    cvShowImage("img_wnd", img);
    cvResizeWindow("img_wnd", 320, 240);
    cvMoveWindow("img_wnd", 0, 0);
    int keyCode = cvWaitKey();
    /*cvReleaseImage(&img);
    cvDestroyWindow("img_wnd");*/
}

void exibir()
{
	for (int i =0; i < images.size();i++){
		int j = i / 5;

		char * wndsName = (char *) malloc(20*sizeof(char));
		sprintf(wndsName,"windows%d",i);
		cout << wndsName << endl;
	    cvNamedWindow(wndsName, 0);
	    cvShowImage(wndsName, images[i]);
	    cvResizeWindow(wndsName, 320, 240);
	    cvMoveWindow(wndsName, 320*(i % 5)+1, 240*j + 10);
	};


	cvWaitKey();
	delete sift;
}

IplImage *carregarImagem(String imgPath, int & height, int & width, int & step, int & channels, uchar *& data)
{
    IplImage *img = cvLoadImage(imgPath.c_str());
    // get the image data
    height = img->height;
    width = img->width;
    step = img->widthStep;
    channels = img->nChannels;
    data = (uchar*)((img->imageData));
    printf("Processing a %dx%d image with %d channels\n", height, width, channels);
    return img;
}

void testeImagemInicial(String imgPath)
{
	int i,j,k;
	int height,width,step,channels;
	uchar *data;

	IplImage *img = cvLoadImage(imgPath.c_str());

	/* armazenando a imagem original para apresentar em cvShowManyImages */
    images.push_back(img);
    //exibirJanelaComImagem(img);
}

void testeInverteImagem(String imgPath)
{
	int i,j,k;
	int height,width,step,channels;
	uchar *data;

	IplImage *img = carregarImagem(imgPath,height, width, step, channels, data);

    // invert the image
    for(i = 0;i < height;i++)
        for(j = 0;j < width;j++)
            for(k = 0;k < channels;k++)
                data[i * step + j * channels + k] = 255 - data[i * step + j * channels + k];

	/* armazenando a imagem original para apresentar em cvShowManyImages */
    images.push_back(img);
    //exibirJanelaComImagem(img);
}


IplImage *converterPretoBraco(IplImage *img)
{
	int height,width,step,channels;

    height = img->height;
    width = img->width;

    IplImage *dst = cvCreateImage( cvSize( width, height ), IPL_DEPTH_8U, 1 );
    cvCvtColor(img, dst, CV_RGB2GRAY); // cimg -> gimg

    return dst;
}

void testeHoughCircles(String imgPath)
{
	//Mat img, gray;

	int i,j,k;
	int height,width,step,channels;
	uchar *data;

    IplImage *img = carregarImagem(imgPath,height, width, step, channels, data);
    IplImage *gray = converterPretoBraco(img);

    cvSmooth(gray, gray, CV_GAUSSIAN, 3, 0 );


	CvMemStorage* circles = cvCreateMemStorage(0);


	CvSeq* c = cvHoughCircles(gray, circles, CV_HOUGH_GRADIENT, 2, gray->height/4, 200, 100 );


	//desenha detectado
    for( i = 0; i < c->total; i++ )
    {
         float* p = (float*)cvGetSeqElem( c , i );
         cvCircle( img, cvPoint(cvRound(p[0]),cvRound(p[1])), cvRound(p[2]), CV_RGB(255,0,0), 3, 8, 0 );
    }

    images.push_back(img);
    //exibirJanelaComImagem(img);

}

void testePBImagem(String imgPath)
{
	int i,j,k;
	int height,width,step,channels;
	uchar *data;

    IplImage *img = carregarImagem(imgPath,height, width, step, channels, data);
    IplImage *dst = converterPretoBraco(img);
    images.push_back(dst);
   // exibirJanelaComImagem(dst);
}

void testeSobel(String imgPath, int xValue, int yValue){
	int i,j,k;
	int height,width,step,channels;
	uchar *data;

    //IplImage *img = carregarImagem(imgPath,height, width, step, channels, data);

    const char* fileAddress= imgPath.c_str();
    IplImage* orginalImage = cvLoadImage(fileAddress,0);
    /*cvNamedWindow("Orginal Image");
    cvShowImage("Orginal Image", orginalImage);*/
    IplImage* edgeImage = cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_16S,1);
    cvSobel(orginalImage,edgeImage,xValue,yValue,7);

    images.push_back(edgeImage);

};

IplImage* testeSobel1(String imgPath, int xValue, int yValue){
	int i,j,k;
	int height,width,step,channels;
	uchar *data;

    //IplImage *img = carregarImagem(imgPath,height, width, step, channels, data);

    const char* fileAddress= imgPath.c_str();
    IplImage* orginalImage = cvLoadImage(fileAddress,0);
    /*cvNamedWindow("Orginal Image");
    cvShowImage("Orginal Image", orginalImage);*/
    IplImage* edgeImage = cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_16S,1);
    cvSobel(orginalImage,edgeImage,xValue,yValue,11);

    double minVal=0.0, maxVal=0.0;
    double scale, shift;
    double min=0, max=4294967295;

    IplImage *preCornerImage32 =cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_32F ,1);
    cvMinMaxLoc( edgeImage, &minVal, &maxVal, NULL, NULL, 0);
    scale = (max - min)/(maxVal-minVal);
    shift = -minVal * scale + min;
    cvConvertScale(edgeImage, preCornerImage32,scale,shift);

    return preCornerImage32;

};
void testeHarrisComSobel(String imgPath){

    double minVal=0.0, maxVal=0.0;
    double scale, shift;
    double min=0, max=255;
    int width,height,step,channels;
    uchar *data;

    IplImage *orginalImage = carregarImagem(imgPath,height, width, step, channels, data);
    IplImage *grayImage = testeSobel1(imgPath,0,1); //cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_8U ,1);

    //cvSmooth(grayImage, grayImage, CV_GAUSSIAN, 11, 0 );

    IplImage *preCornerImage32 =cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_32F ,1);
    IplImage *preCornerImage8 =cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_8U ,1);
    cvCornerHarris(grayImage,preCornerImage32,7);
    cvMinMaxLoc( preCornerImage32, &minVal, &maxVal, NULL, NULL, 0);
    scale = (max - min)/(maxVal-minVal);
    shift = -minVal * scale + min;
    cvConvertScale(preCornerImage32, preCornerImage8,scale,shift);

    images.push_back(preCornerImage8);
};

void testeHarris(String imgPath, int xValue, int yValue){

    double minVal=0.0, maxVal=0.0;
    double scale, shift;
    double min=0, max=255;
    int width,height,step,channels;
    uchar *data;

    IplImage *orginalImage = carregarImagem(imgPath,height, width, step, channels, data);
    IplImage *grayImage = converterPretoBraco(orginalImage); //cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_8U ,1);

    cvSmooth(grayImage, grayImage, CV_GAUSSIAN, xValue, yValue );

    IplImage *preCornerImage32 =cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_32F ,1);
    IplImage *preCornerImage8 =cvCreateImage(cvGetSize(orginalImage),IPL_DEPTH_8U ,1);
    cvCornerHarris(grayImage,preCornerImage32,7);
    cvMinMaxLoc( preCornerImage32, &minVal, &maxVal, NULL, NULL, 0);
    scale = (max - min)/(maxVal-minVal);
    shift = -minVal * scale + min;
    cvConvertScale(preCornerImage32, preCornerImage8,scale,shift);

    images.push_back(preCornerImage8);
};

int testeSIFT( const char * path )
{
	// Create an instance of SIFT

	sift = new my::SIFT(path, 8, 4,1.0);


	sift->DoSift();				// Find keypoints
	sift->ShowAbsSigma();		// Display the sigma table

	sift->ShowKeypoints();		// Show the keypoints
	sift->ShowKeypointsCleanImage();
	sift->testeHoughCircles();

	//cvWaitKey(0);				// Wait for a keypress

	// Cleanup and exit
	//delete sift;
	return 0;
}

void show(){
	for (int var = 0; var < images.size(); ++var) {
		exibirJanelaComImagem(images[var]);
	}
}

void mainTestes(String path)
{
	cout << "void mainTestes("+path+")"<<endl;
	//testeImagemInicial(path);
//    testeInverteImagem(path);
     testeHoughCircles(path);
//    testeSobel(path,0,1);
    //testeSobel(path,1,0);
    //testeHarris(path,1,0);
    testeSIFT(path.c_str());
    //testeHarrisComSobel(path);
    cout << "void mainTestes("+path+")"<<endl;

    /*
     * Tiramos daqui pois esta problematico para apresentar
     * */
    //testePBImagem(path);
}

/**
Main
*/

void comparaImagens(){
	// Create an instance of SIFT

	my::SIFT * probe = new my::SIFT("./logos/teste/ubuntu.jpg", 8, 4,1.0);
	my::SIFT * imagemBase = new my::SIFT("./logos/teste/ubuntu.jpg", 8, 4,1.0);
	probe->handle = "Probe";
	imagemBase->handle = "imagemBase";


	probe->DoSift();				// Find keypoints
	probe->ShowAbsSigma();		// Display the sigma table
	probe->ShowKeypoints();		// Show the keypoints

	imagemBase->DoSift();				// Find keypoints
	imagemBase->ShowAbsSigma();		// Display the sigma table
	imagemBase->ShowKeypoints();		// Show the keypoints
	imagemBase->compara(probe);

};

// @DEPRECATED - a main agora esta na classe logo matcher
int mai1n( int argc, char** argv )
{

	cout << "testando SVN";
	//comparaImagens();

	cvWaitKey();


	return 0;
}


